# -*- coding: cp1252 -*-
"""
copyright (c) 2009 Claudio Canepa
this code licensed under the new BSD license, see file license_simpledocu.txt
"""
#necesitamos un filtro que haga wrap to indentation.
import sys, os

import re
import collections

def get_args():
    import optparse
    usage_desc =(
    """%prog [options] [src_files [dest_dir]]

    src_files is a list of the filenames to wrap, semicolon separated.

    if dest_dir not provided, output goes to stdout.
    if both positional arguments are omited, input is taken from stdin,
    out goes to stdout.
    if both are present, then for each file in the src_files list,
    the file is wraped and written under dest_dir, with the same shortname.
    """)

                
    parser = optparse.OptionParser(usage=usage_desc)
    parser.add_option("--tabsize", dest="tabsize", type='int', default=4,
                      help="num spaces per tab")
    parser.add_option("--maxwidth", dest="maxwidth", type='int', default=75,
                      help="desired max line width")
    
    (options, args) = parser.parse_args()

    # preprocess and validate

    #   generic sanity
    if len(args)>2:
        parser.error("unknown extra arguments or options")
    if options.maxwidth<1:
        parser.error("maxwidth must be at least 1")

    #   resolve dest dir
    if len(args)==2:
        dest_dir = args[1]
        if os.path.exists(dest_dir):
            ok = os.path.isdir(dest_dir)
            if not ok:
                parser.error("there exist a file named the same as dest_dir, cannot create the destination directory")
        else:
            # new dir
            try:
                os.mkdir(dest_dir) #? mode left as an exercise
            except:
                parser.error("unable to create directory dest_dir")
    else:
        dest_dir = None

    #   resolve src_list, ';' filename separators
    if len(args)>0:
        src_list = args[0]
        fnames = src_list.split(';')
        #? more validation here ?
    else:
        fnames = []

    return fnames , dest_dir, options.tabsize, options.maxwidth 

# non automatic test for get_args
def test_get_args():
    fnames, dest_dir, tabsize, maxwidth = get_args()
    print ' dest dir:',dest_dir
    print '  tabsize:',tabsize
    print 'maxwidth:',maxwidth
    print 'filenames:'
    for e in fnames:
        print e
    print '*** done ***'

# lo que necesitamos mas rapido es:
##    teniendo los .bst en un dir bajo svn,
##    hacer svn --status
##    capturar los fnames de esa salida
##    vaciar el directorio changed
##    hacer iwrap de cada uno de estos files, dando salida hacia changed
##    despues manualmente copiariamos los files a google site y hariamos un commit.
##    (la carpeta changed no tiene que estar bajo control svn)



### implementacion parcial rapida:
##def main():
##    simpletags = {'+':1,'w':1,'?':1}
##
##    fnames , dest_dir, tabsize, maxwidth = get_args()
##    if not fnames or not dest_dir:
##        print '*** Not implemented !!! ***'
##        sys.exit(1)
##
##    
##    for fname in fnames:
##        text_in = text_from_file(fname)
##        text_out = helper_get_iwrapped_text(text_in, tabsize, maxwidth, simpletags)
##        fnameout = os.path.join(dest_dir,os.path.basename(fname))
##        text_to_file( text_out, fnameout)
        



# --> funcionalidad text

# asumes tab expansion was done; asumes at least one leading space taken from IDLE
num_lead_spaces_aux = lambda s, c=re.compile(r"^(\s*)(\s)"): c.match(s).end()
def num_lead_spaces(s): # asumes len(s)>0
    if s[0]!=' ':
        return 0
    else:
        return num_lead_spaces_aux(s)

# returns None if <*pre> not found as first significative noblank, where *='' or '/'
# returns a re.MatchObject if found.
# if match, match.group(1) will be '' if no bar, '/' if present
# if match,  match.end() will point after the '>'
# note that no whitespace is allowed between the brackets.
scan_pre = lambda s, c=re.compile(r"(?:^\s*<)(/?)(?:pre>)", re.IGNORECASE): c.match(s) 

# this really is customized for iwrap function, other usage is discouraged
#asumes tab conversion and other preprocess had done. Dont send tabs here!
#It is known that len(line)>width
#required : line ends with one an only one  \n
#guaranties about the return value:
#None of the returning lines will have a trailing \n
#Will break only on spaces 
#All lines returned have at most 'width' characters, except for lines
#containing a single word (no spaces) wich has more characters than 'width'
#all lines begin with a non space character
#the concatenation of lines in result is equal to the original, except
#will not have trailing /n and some spaces between worlds may be missing.

def _wrap_line(line,width):
    #line = line[:-1] # strip trailing \n
    softlines = collections.deque()
    start = 0
    linelen = len(line)-1 # as if we stripped the trailing \n
    while start<linelen:        
        end = start + width
        if end<linelen:
            k = line.rfind(' ',start,end+1) #rfind dont test last pos
        else:
            k = linelen
        if k<0:
            # a world longer than width
            k = line.find(' ',end,linelen+1)
            if k<0:
                # no spaces till the end of line
                k = linelen
        part = line[start:k]
        start += len(part)
        softlines.append(part)
        # eat spaces following a softline
        try:
            skip = num_lead_spaces_aux(line[start:])
        except AttributeError:
            skip = 0
        start += skip 
    return softlines
            
            
#asumes <pre> and </pre> are only preceded by whitespace
#trailing characters after those tags are ignored, ie not send to outfile
#does not wrap lines into a <pre> block
#simpletags a iterable giving the one-letter tags
# for raw_line in infile.readline: -> refactored foe unittest easyness
def show(msg,s): #for debug
    if len(s) and s[-1]=='\n':
        s = s[:-1]
    print '%s:|%s|'%(msg,s)
    
def iwrap( in_line_iterator, out_write, tabsize, maxwidth, simpletags):
    tabsize0 = tabsize
    cntpre = 0
    while 1:
        try:
            raw_line = in_line_iterator.next()
        except StopIteration:
            break
        # uniformize: linex always ends with \n
        #? eliminated tentativelly -> and have at least 1 leading space
        #? do here some cleanup, like '\r' ?
        linex = raw_line.expandtabs(tabsize)
        if linex[-1]!='\n':
            linex += '\n'
        #show('working on linex',linex) #debug
        match = scan_pre(linex)
        parts = collections.deque()
        if not (match is None):
            #line has a leading (minus indentation) <*pre>
            #update cntpre and disregard extra characters
            if match.group(1)=='/':
                cntpre -= 1
            else:
                cntpre += 1
            parts.append(linex[:match.end()]+'\n')
            # done with <*pre> lines
        else:               
            #line has no leading <*pre>
            if cntpre:
                #into a pre block, dont wrap
                parts.append(linex)
            else:
                #out of any pre, consider wrap
                if len(linex)<=maxwidth:
                    #no need for wrap
                    parts.append(linex)
                else:
                    #yup, need wrap
                    indent = num_lead_spaces(linex)
                    if linex[indent] in simpletags:
                        if linex[indent+1]==' ':
                            # really a simpletag
                            indent = indent + 1 + num_lead_spaces(linex[indent+1:])
                    available = maxwidth - indent
                    assert(available>0) #? indentation bumping maxwidth is bad ?
                    #solve as dedent, wrap, reindent
                    head = linex[:indent]
                    tail = linex[indent:]
                    tailparts = _wrap_line(tail, available)
                    #  reasemble / reindent. fist subline can have a simpletag
                    parts.append(''.join([head, tailparts[0], '\n']))
                    tailparts.popleft()
                    
                    filling = ' '*indent #? make a pool?
                    for e in tailparts:
                        parts.append(''.join([filling, e, '\n']))
        #out the parts
        for e in parts:
            out_write(e)

if __name__ == "__main__":
    #test_get_args()
    test2_wrap_line()
    
